package org.jeecg.modules.system.controller;

import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.dypnsapi20170525.models.GetAuthTokenRequest;
import com.aliyun.dypnsapi20170525.models.GetAuthTokenResponse;
import com.aliyun.dypnsapi20170525.models.VerifyPhoneWithTokenRequest;
import com.aliyun.dypnsapi20170525.models.VerifyPhoneWithTokenResponse;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.google.gson.Gson;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.jeecg.common.constant.CacheConstant;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.*;
import org.jeecg.common.util.encryption.EncryptedString;
import org.jeecg.common.util.sms.AliYunUtil;
import org.jeecg.config.JeecgBaseConfig;
import org.jeecg.modules.base.service.BaseCommonService;
import org.jeecg.modules.nikeProject.service.ISportsProjectService;
import org.jeecg.modules.system.entity.*;
import org.jeecg.modules.system.entity.vo.RegisterVOReq;
import org.jeecg.modules.system.model.AppLoginModel;
import org.jeecg.modules.system.model.SysLoginModel;
import org.jeecg.modules.system.service.*;
import org.jeecg.modules.system.service.impl.SysBaseApiImpl;
import org.jeecg.modules.system.util.RandImageUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.util.*;

/**
 * @Author scott
 * @since 2018-12-17
 */
@RestController
@RequestMapping("/sys")
@Api(tags="101-用户登录")
@Slf4j
public class LoginController {
	@Autowired
	private ISysUserService sysUserService;

	@Autowired
	private ISysRoleService sysRoleService;

	@Autowired
	private ISportsProjectService sportsProjectService;

	@Autowired
	private ISysPermissionService sysPermissionService;
	@Autowired
	private SysBaseApiImpl sysBaseApi;
	@Autowired
	private ISysLogService logService;
	@Autowired
    private RedisUtil redisUtil;
	@Autowired
    private ISysDepartService sysDepartService;
	@Autowired
    private ISysDictService sysDictService;
	@Resource
	private BaseCommonService baseCommonService;

	@Autowired
	private JeecgBaseConfig jeecgBaseConfig;

	private final String BASE_CHECK_CODES = "qwertyuiplkjhgfdsazxcvbnmQWERTYUPLKJHGFDSAZXCVBNM1234567890";

	/**
	 * pc登录
	 * @param req
	 * @return
	 * @throws Exception
	 */

	@AutoLog("PC登录接口")
	@ApiOperation("PC登录接口")
	@RequestMapping(value = "/login", method = RequestMethod.POST)
	public Result<?> login(@RequestBody RegisterVOReq req){
		Result<?> result;
		String username = req.getUsername();
		String password = req.getPassword();
		SysUser sysUser = sysUserService.getUserByName(username);
		result = sysUserService.checkUserIsEffective(sysUser);
		if(!result.isSuccess()) {
			return result;
		}

		//2. 校验用户名或密码是否正确
		String userpassword = PasswordUtil.encrypt(username, password, sysUser.getSalt());
		String syspassword = sysUser.getPassword();
		if (!syspassword.equals(userpassword)) {
			result.error500("密码错误！");
			return result;
		}
		//添加日志
		baseCommonService.addLog("用户名: " + sysUser.getUsername() + ",登录成功！", CommonConstant.LOG_TYPE_1, null);
		//用户登录信息
		return userInfo(sysUser, result);
	}

	@AutoLog("发送验证码至邮箱")
	@ApiOperation(value = "发送验证码至邮箱", notes = "发送验证码至邮箱")
	@GetMapping("/sendVerifyCodeToEmail")
	public Result<String> sendVerifyCodeToEmail(@RequestParam String email){
		Result<String> result = new Result<>();
		String redisKey = CommonConstant.EMAIL_REDIS_KEY + email;
		Object object = redisUtil.get(redisKey);
		if (object != null) {
			result.setMessage("验证码10分钟内，仍然有效！");
			result.setSuccess(false);
			return result;
		}
		String captcha = RandomUtil.randomNumbers(6);
		log.info("邮箱验证码：{}", captcha);
		boolean flag = EmailUtil.sendEmail(email, captcha);
//		boolean flag = true;
		if (!flag){
			result.setMessage("发送邮箱验证码失败！");
			result.setSuccess(false);
			return result;
		}

		//验证码10分钟内有效
		redisUtil.set(redisKey, captcha, 600);
		result.setSuccess(true);
		result.setMessage("邮箱验证码发送成功！");
		return result;
	}

	/**
	 * 检查邮箱验证码是否正确  邮箱号+邮箱验证码
	 * @param req
	 * @return
	 */
	@AutoLog("检查邮箱验证码是否正确")
	@ApiOperation(value = "检查邮箱验证码是否正确", notes = "检查邮箱验证码是否正确")
	@PostMapping("/checkEmailVerifyCode")
	public Result<String> checkEmailVerifyCode(@RequestBody RegisterVOReq req){
		Result<String> result = new Result<>();
		String redisKey = CommonConstant.EMAIL_REDIS_KEY + req.getEmail();
		String verifyCode = (String) redisUtil.get(redisKey);
		if (verifyCode != null && !verifyCode.isEmpty()){
			if (verifyCode.equals(req.getCaptcha())){
				result.setSuccess(true);
				result.setMessage("邮箱验证成功！");
			} else {
				result.setSuccess(false);
				result.setMessage("邮箱验证码输入错误，请重新输入！");
			}
		} else {
			result.setSuccess(false);
			result.setMessage("邮箱验证码无效，请重新获取！");
		}
		return result;
	}

	/**
	 * 用于未登陆时进行密码重置  需要通过邮箱验证码
	 * @param req
	 * @return
	 */
	@AutoLog("用于未登陆时进行密码重置")
	@ApiOperation("用于未登陆时进行密码重置")
	@PostMapping("/resetPassword")
	public Result<String> resetPassword(@RequestBody RegisterVOReq req){
		Result<String> result = new Result<>();
		//查找当前用户
		String email = req.getEmail();
		SysUser sysUser = sysUserService.getUserByEmail(email);
		if(sysUser==null){
			result.setMessage("当前用户不存在！请重新注册！");
			result.setSuccess(false);
			return result;
		}
		// 判断两次输入密码是否一致
		if (!req.getPassword().equals(req.getConfirmPassword())){
			result.setMessage("两次密码输入不一致，请重新输入！");
			result.setSuccess(false);
			return result;
		}
		String salt = sysUser.getSalt();
		String passwordEncode = PasswordUtil.encrypt(sysUser.getPhone(), req.getPassword(), salt);
		sysUser.setPassword(passwordEncode);
		sysUserService.updateById(sysUser);
		result.setMessage("密码重置成功！");
		result.setSuccess(true);
		return result;
	}

	/*@ApiOperation("PC登录接口")
	@RequestMapping(value = "/login", method = RequestMethod.POST)
	public Result<JSONObject> login(@RequestBody SysLoginModel sysLoginModel){
		Result<JSONObject> result = new Result<JSONObject>();
		String username = sysLoginModel.getUsername();
		String password = sysLoginModel.getPassword();
		//update-begin--Author:scott  Date:20190805 for：暂时注释掉密码加密逻辑，有点问题
		//前端密码加密，后端进行密码解密
		//password = AesEncryptUtil.desEncrypt(sysLoginModel.getPassword().replaceAll("%2B", "\\+")).trim();//密码解密
		//update-begin--Author:scott  Date:20190805 for：暂时注释掉密码加密逻辑，有点问题

		//update-begin-author:taoyan date:20190828 for:校验验证码
        String captcha = sysLoginModel.getCaptcha();
        if(captcha==null){
            result.error500("验证码无效");
            return result;
        }
        String lowerCaseCaptcha = captcha.toLowerCase();
        //update-begin-author:taoyan date:2022-9-13 for: VUEN-2245 【漏洞】发现新漏洞待处理20220906
		// 加入密钥作为混淆，避免简单的拼接，被外部利用，用户自定义该密钥即可
        String origin = lowerCaseCaptcha+sysLoginModel.getCheckKey()+jeecgBaseConfig.getSignatureSecret();
		String realKey = Md5Util.md5Encode(origin, "utf-8");
		//update-end-author:taoyan date:2022-9-13 for: VUEN-2245 【漏洞】发现新漏洞待处理20220906
		Object checkCode = redisUtil.get(realKey);
		//当进入登录页时，有一定几率出现验证码错误 #1714
		if(checkCode==null || !checkCode.toString().equals(lowerCaseCaptcha)) {
            log.warn("验证码错误，key= {} , Ui checkCode= {}, Redis checkCode = {}", sysLoginModel.getCheckKey(), lowerCaseCaptcha, checkCode);
			result.error500("验证码错误");
			// 改成特殊的code 便于前端判断
			result.setCode(HttpStatus.PRECONDITION_FAILED.value());
			return result;
		}
		//update-end-author:taoyan date:20190828 for:校验验证码
		
		//1. 校验用户是否有效
		//update-begin-author:wangshuai date:20200601 for: 登录代码验证用户是否注销bug，if条件永远为false
		LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
		queryWrapper.eq(SysUser::getUsername,username);
		SysUser sysUser = sysUserService.getOne(queryWrapper);
		//update-end-author:wangshuai date:20200601 for: 登录代码验证用户是否注销bug，if条件永远为false
		result = sysUserService.checkUserIsEffective(sysUser);
		if(!result.isSuccess()) {
			return result;
		}

		//2. 校验用户名或密码是否正确
		String userpassword = PasswordUtil.encrypt(username, password, sysUser.getSalt());
		String syspassword = sysUser.getPassword();
		if (!syspassword.equals(userpassword)) {
			result.error500("用户名或密码错误");
			return result;
		}
				
		//用户登录信息
		userInfo(sysUser, result);
		//update-begin--Author:liusq  Date:20210126  for：登录成功，删除redis中的验证码
		redisUtil.del(realKey);
		//update-begin--Author:liusq  Date:20210126  for：登录成功，删除redis中的验证码
		LoginUser loginUser = new LoginUser();
		BeanUtils.copyProperties(sysUser, loginUser);
		baseCommonService.addLog("用户名: " + username + ",登录成功！", CommonConstant.LOG_TYPE_1, null,loginUser);
        //update-end--Author:wangshuai  Date:20200714  for：登录日志没有记录人员
		return result;
	}*/

	/**
	 * app登录
	 * @AppLoginModel appLoginModel
	 * @return
	 * @throws Exception
	 */
//	@ApiOperation("APP登录接口")
	@RequestMapping(value = "/mLogin", method = RequestMethod.POST)
	public Result<?> mLogin(@RequestBody AppLoginModel appLoginModel) throws Exception {
		Result<?> result = new Result();

		String username = appLoginModel.getUsername();
		String password =  appLoginModel.getPassword();
		//1. 校验用户是否有效
		SysUser sysUser = sysUserService.getUserByName(username);
		result = sysUserService.checkUserIsEffective(sysUser);
		if(!result.isSuccess()) {
			return result;
		}

		//2. 校验密码是否正确
		String userpassword = PasswordUtil.encrypt(username, password, sysUser.getSalt());
		String syspassword = sysUser.getPassword();
		if (!syspassword.equals(userpassword)) {
			result.error500("密码错误");
			return result;
		}
		//VOC项目不需要部门信息
		/*String orgCode = sysUser.getOrgCode();
		if(oConvertUtils.isEmpty(orgCode)) {
			//如果当前用户无选择部门 查看部门关联信息
			List<SysDepart> departs = sysDepartService.queryUserDeparts(sysUser.getId());
			//update-begin-author:taoyan date:20220117 for: JTC-1068【app】新建用户，没有设置部门及角色，点击登录提示暂未归属部，一直在登录页面 使用手机号登录 可正常
			if (departs == null || departs.size() == 0) {
				*//*result.error500("用户暂未归属部门,不可登录!");
				return result;*//*
			}else{
				orgCode = departs.get(0).getOrgCode();
				sysUser.setOrgCode(orgCode);
				this.sysUserService.updateUserDepart(username, orgCode);
			}
			//update-end-author:taoyan date:20220117 for: JTC-1068【app】新建用户，没有设置部门及角色，点击登录提示暂未归属部，一直在登录页面 使用手机号登录 可正常
		}*/
		SysRole role = sysRoleService.getRoleByUserId(sysUser.getId());


		//用户登录信息
		JSONObject obj = new JSONObject();
		obj.put("userInfo", sysUser);
		obj.put("roleInfo",role);

		// 生成token
		String token = JwtUtil.sign(username, syspassword);
		// 设置超时时间
		redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token);
		redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME*24*30 / 1000); //app 24小时*30

		//token 信息
		obj.put("token", token);
		baseCommonService.addLog("用户名: " + username + ",登录成功[移动端]！", CommonConstant.LOG_TYPE_1, null);
		return Result.error("success", obj);
	}


	/**
	 * 新短信登陆接口
	 * @param jsonObject
	 * @return
	 */
	@ApiOperation("新短信登录接口")
	@PostMapping(value = "/sms")
	public Result<String> sms(@RequestBody JSONObject jsonObject, HttpServletRequest request) {
		Result<String> result = new Result<String>();
		String clientIp = IpUtils.getIpAddr(request);
		String mobile = jsonObject.get("mobile").toString();
		log.info(mobile);
		if(oConvertUtils.isEmpty(mobile)){
			result.setMessage("手机号不允许为空！");
			result.setSuccess(false);
			return result;
		}

		String redisKey = CommonConstant.PHONE_REDIS_KEY_PRE + mobile;
		Object object = redisUtil.get(redisKey);
		if (object != null) {
			result.setMessage("验证码10分钟内，仍然有效！");
			result.setSuccess(false);
			return result;
		}
		//增加check ip地址 防止恶意刷短信接口
		if (!DySmsLimit.canSendSms(clientIp)){
			log.warn("--------[警告] IP地址：{}，短信接求请求太多------------", clientIp);
			result.setMessage("短信接口请求太多，请稍候再试！");
			result.setCode(CommonConstant.PHONE_SMS_FAIL_CODE);
			result.setSuccess(false);
			return result;
		}
		//随机数
//		String captcha = RandomUtil.randomNumbers(6);
		String captcha = "111111";
		JSONObject obj = new JSONObject();
		obj.put("code", captcha);
		//登录模板
		try {
//			boolean flag = DySmsHelper.sendSms(mobile, obj, DySmsEnum.LOGIN_CERT_TEMPLATE_CODE);
			boolean flag = true;
//			String content = "【活力校园】您的验证码为：" + captcha + "，请勿泄露于他人！";
//			boolean flag = SmsUtil.sendSms(mobile, content);
			if (flag == false) {
				result.setMessage("短信验证码发送失败,请稍后重试");
				result.setSuccess(false);
				return result;
			}

			//验证码10分钟内有效
			redisUtil.set(redisKey, captcha, 600);
			//} catch (ClientException e) {
		}catch(Exception e){
			e.printStackTrace();
			result.error500(" 短信接口未配置，请联系管理员！");
			return result;
		}
		return result;
	}

	/**
	 * 短信登录接口
	 *
	 * @param jsonObject
	 * @return
	 */
//	@ApiOperation("短信登录接口")
//	@PostMapping(value = "/sms")
	public Result<?> sms1(@RequestBody JSONObject jsonObject) {
		Result<?> result = new Result();
		String mobile = jsonObject.get("mobile").toString();
		//手机号模式 登录模式: "2"  注册模式: "1"
		String smsmode =jsonObject.get("smsmode").toString();
		log.info(mobile);
		if(oConvertUtils.isEmpty(mobile)){
			result.setMessage("手机号不允许为空！");
			result.setSuccess(false);
			return result;
		}

		//update-begin-author:taoyan date:2022-9-13 for: VUEN-2245 【漏洞】发现新漏洞待处理20220906
		String redisKey = CommonConstant.PHONE_REDIS_KEY_PRE + mobile;
		Object object = redisUtil.get(redisKey);
		//update-end-author:taoyan date:2022-9-13 for: VUEN-2245 【漏洞】发现新漏洞待处理20220906

		if (object != null) {
			result.setMessage("验证码10分钟内，仍然有效！");
			result.setSuccess(false);
			return result;
		}

		//随机数
		String captcha = RandomUtil.randomNumbers(6);
		JSONObject obj = new JSONObject();
		obj.put("code", captcha);
		try {
			boolean b = false;
			//注册模板
			if (CommonConstant.SMS_TPL_TYPE_1.equals(smsmode)) {
				SysUser sysUser = sysUserService.getUserByPhone(mobile);
				if(sysUser!=null) {
					result.error500(" 手机号已经注册，请直接登录！");
					baseCommonService.addLog("手机号已经注册，请直接登录！", CommonConstant.LOG_TYPE_1, null);
					return result;
				}
				b = DySmsHelper.sendSms(mobile, obj, DySmsEnum.REGISTER_TEMPLATE_CODE);
//				b = true;
			}else {
				//登录模式，校验用户有效性
				SysUser sysUser = sysUserService.getUserByPhone(mobile);
				result = sysUserService.checkUserIsEffective(sysUser);
				if(!result.isSuccess()) {
					String message = result.getMessage();
					String userNotExist="该用户不存在，请注册";
					if(userNotExist.equals(message)){
						result.error500("该用户不存在或未绑定手机号");
					}
					return result;
				}

				/**
				 * smsmode 短信模板方式  0 .登录模板、1.注册模板、2.忘记密码模板
				 */
				/*if (CommonConstant.SMS_TPL_TYPE_0.equals(smsmode)) {
					//登录模板
					b = DySmsHelper.sendSms(mobile, obj, DySmsEnum.LOGIN_TEMPLATE_CODE);
				} else if(CommonConstant.SMS_TPL_TYPE_2.equals(smsmode)) {
					//忘记密码模板
					b = DySmsHelper.sendSms(mobile, obj, DySmsEnum.FORGET_PASSWORD_TEMPLATE_CODE);
				}*/
				b = true;
			}

			if (b == false) {
				result.setMessage("短信验证码发送失败,请稍后重试");
				result.setSuccess(false);
				return result;
			}

			//update-begin-author:taoyan date:2022-9-13 for: VUEN-2245 【漏洞】发现新漏洞待处理20220906
			//验证码10分钟内有效
			redisUtil.set(redisKey, captcha, 600);
			//update-end-author:taoyan date:2022-9-13 for: VUEN-2245 【漏洞】发现新漏洞待处理20220906

			//update-begin--Author:scott  Date:20190812 for：issues#391
			//result.setResult(captcha);
			//update-end--Author:scott  Date:20190812 for：issues#391
			result.setSuccess(true);

		//} catch (ClientException e) {
		} catch (Exception e) {
			e.printStackTrace();
			result.error500(" 短信接口未配置，请联系管理员！");
			return result;
		}
		return result;
	}


	/**
	 * 手机号登录接口
	 *
	 * @param jsonObject
	 * @return
	 */
	@ApiOperation("手机号登录接口")
	@PostMapping("/phoneLogin")
	public Result<?> phoneLogin(@RequestBody JSONObject jsonObject) {
		Result<?> result = new Result();
		String phone = jsonObject.getString("mobile");

		//校验用户有效性
		SysUser sysUser = sysUserService.getUserByPhone(phone);
		if (sysUser != null) {
			result = sysUserService.checkUserIsEffective(sysUser);
			if(!result.isSuccess()) {
				return result;
			}
		} else {
			sysUser = sysUserService.getUserByName(phone);
			if (sysUser != null) {
				result = sysUserService.checkUserIsEffective(sysUser);
				if (!result.isSuccess()) {
					return result;
				}
			} else {
				//不存在则创建新用户,然后查询用户信息供获取token用
				sysUserService.addUser(phone);
				sysUser = sysUserService.getUserByPhone(phone);
			}
		}

//		String smscode = jsonObject.getString("captcha");
		//update-begin-author:taoyan date:2022-9-13 for: VUEN-2245 【漏洞】发现新漏洞待处理20220906
//		String redisKey = CommonConstant.PHONE_REDIS_KEY_PRE + phone;
//		Object code = redisUtil.get(redisKey);
		//update-end-author:taoyan date:2022-9-13 for: VUEN-2245 【漏洞】发现新漏洞待处理20220906

//		if (!smscode.equals(code)) {
//			result.setMessage("手机验证码错误");
//			result.setSuccess(false);
//			return result;
//		}
		//用户信息
		userInfo(sysUser, result);
		//添加日志
		baseCommonService.addLog("用户名: " + sysUser.getUsername() + ",登录成功！", CommonConstant.LOG_TYPE_1, null);
		return result;
	}

	/**
	 * 更新用户首次登陆状态 0-是 1-否
	 * @param request
	 * @return
	 */
	@ApiOperation("更新首次登陆状态")
	@GetMapping("/updateUserFirstLogin")
	public Result<?> updateUserFirstLogin(HttpServletRequest request){
		String username = JwtUtil.getUserNameByToken(request);
		SysUser sysUser = sysUserService.getUserByName(username);
		sysUser.setIzFirstLogin("1");
		sysUser.setUpdateBy(username);
		sysUser.setUpdateTime(new Date());
		boolean flag = sysUserService.updateById(sysUser);
		if (!flag){
			return Result.error("更新首次登陆状态失败！");
		}
		return Result.OK("更新首次登陆状态成功！");
	}

	/**
	 * 后台生成图形验证码 ：有效
	 * @param response
	 * @param key
	 */
	@ApiOperation("获取验证码")
	@GetMapping(value = "/randomImage/{key}")
	public Result<String> randomImage(HttpServletResponse response,@PathVariable("key") String key){
		Result<String> res = new Result<String>();
		try {
			//生成验证码
			String code = RandomUtil.randomString(BASE_CHECK_CODES,4);
			//存到redis中
			String lowerCaseCode = code.toLowerCase();

			//update-begin-author:taoyan date:2022-9-13 for: VUEN-2245 【漏洞】发现新漏洞待处理20220906
			// 加入密钥作为混淆，避免简单的拼接，被外部利用，用户自定义该密钥即可
			String origin = lowerCaseCode + key + jeecgBaseConfig.getSignatureSecret();
			log.info("origin=" + origin);
			String realKey = Md5Util.md5Encode(origin, "utf-8");
			//update-end-author:taoyan date:2022-9-13 for: VUEN-2245 【漏洞】发现新漏洞待处理20220906

			redisUtil.set(realKey, lowerCaseCode, 60);
			log.info("获取验证码，Redis key = {}，checkCode = {}", realKey, code);
			//返回前端
			String base64 = RandImageUtil.generate(code);
			res.setSuccess(true);
			res.setResult(base64);
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			res.error500("获取验证码失败,请检查redis配置!");
			return res;
		}
		return res;
	}


	/**
	 * 退出登录
	 * @param request
	 * @param response
	 * @return
	 */
	@ApiOperation("退出登录接口")
	@GetMapping(value = "/logout")
	public Result<Object> logout(HttpServletRequest request,HttpServletResponse response) {
		//用户退出逻辑
		String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
		if(oConvertUtils.isEmpty(token)) {
			return Result.error("退出登录失败！");
		}
		String username = JwtUtil.getUsername(token);
		LoginUser sysUser = sysBaseApi.getUserByName(username);
		if(sysUser!=null) {
			//update-begin--Author:wangshuai  Date:20200714  for：登出日志没有记录人员
			baseCommonService.addLog("用户名: "+sysUser.getRealname()+",退出成功！", CommonConstant.LOG_TYPE_1, null,sysUser);
			//update-end--Author:wangshuai  Date:20200714  for：登出日志没有记录人员
			log.info(" 用户名:  "+sysUser.getRealname()+",退出成功！ ");
			//清空用户登录Token缓存
			redisUtil.del(CommonConstant.PREFIX_USER_TOKEN + token);
			//清空用户登录Shiro权限缓存
			redisUtil.del(CommonConstant.PREFIX_USER_SHIRO_CACHE + sysUser.getId());
			//清空用户的缓存信息（包括部门信息），例如sys:cache:user::<username>
			redisUtil.del(String.format("%s::%s", CacheConstant.SYS_USERS_CACHE, sysUser.getUsername()));
			//调用shiro的logout
			SecurityUtils.getSubject().logout();
			return Result.ok("退出登录成功！");
		}else {
			return Result.error("Token无效!");
		}
	}

	/**
	 * 【vue3专用】获取用户信息
	 */
	@GetMapping("/user/getUserInfo")
	public Result<JSONObject> getUserInfo(HttpServletRequest request){
		Result<JSONObject> result = new Result<JSONObject>();
		String  username = JwtUtil.getUserNameByToken(request);
		if(oConvertUtils.isNotEmpty(username)) {
			SysUser sysUser = sysUserService.getUserByName(username);
			JSONObject obj=new JSONObject();
			obj.put("userInfo",sysUser);
			obj.put("sysAllDictItems", sysDictService.queryAllDictItems());
			result.setResult(obj);
			result.success("");
		}
		return result;

	}

	/**
	 * 获取访问量
	 * @return
	 */
	@GetMapping("loginfo")
	public Result<JSONObject> loginfo() {
		Result<JSONObject> result = new Result<JSONObject>();
		JSONObject obj = new JSONObject();
		//update-begin--Author:zhangweijian  Date:20190428 for：传入开始时间，结束时间参数
		// 获取一天的开始和结束时间
		Calendar calendar = new GregorianCalendar();
		calendar.set(Calendar.HOUR_OF_DAY, 0);
		calendar.set(Calendar.MINUTE, 0);
		calendar.set(Calendar.SECOND, 0);
		calendar.set(Calendar.MILLISECOND, 0);
		Date dayStart = calendar.getTime();
		calendar.add(Calendar.DATE, 1);
		Date dayEnd = calendar.getTime();
		// 获取系统访问记录
		Long totalVisitCount = logService.findTotalVisitCount();
		obj.put("totalVisitCount", totalVisitCount);
		Long todayVisitCount = logService.findTodayVisitCount(dayStart,dayEnd);
		obj.put("todayVisitCount", todayVisitCount);
		Long todayIp = logService.findTodayIp(dayStart,dayEnd);
		//update-end--Author:zhangweijian  Date:20190428 for：传入开始时间，结束时间参数
		obj.put("todayIp", todayIp);
		result.setResult(obj);
		result.success("登录成功");
		return result;
	}
	
	/**
	 * 获取访问量
	 * @return
	 */
	@GetMapping("visitInfo")
	public Result<List<Map<String,Object>>> visitInfo() {
		Result<List<Map<String,Object>>> result = new Result<List<Map<String,Object>>>();
		Calendar calendar = new GregorianCalendar();
		calendar.set(Calendar.HOUR_OF_DAY,0);
        calendar.set(Calendar.MINUTE,0);
        calendar.set(Calendar.SECOND,0);
        calendar.set(Calendar.MILLISECOND,0);
        calendar.add(Calendar.DAY_OF_MONTH, 1);
        Date dayEnd = calendar.getTime();
        calendar.add(Calendar.DAY_OF_MONTH, -7);
        Date dayStart = calendar.getTime();
        List<Map<String,Object>> list = logService.findVisitCount(dayStart, dayEnd);
		result.setResult(oConvertUtils.toLowerCasePageList(list));
		return result;
	}
	
	
	/**
	 * 登陆成功选择用户当前部门
	 * @param user
	 * @return
	 */
	@RequestMapping(value = "/selectDepart", method = RequestMethod.PUT)
	public Result<JSONObject> selectDepart(@RequestBody SysUser user) {
		Result<JSONObject> result = new Result<JSONObject>();
		String username = user.getUsername();
		if(oConvertUtils.isEmpty(username)) {
			LoginUser sysUser = (LoginUser)SecurityUtils.getSubject().getPrincipal();
			username = sysUser.getUsername();
		}
		String orgCode= user.getOrgCode();
		this.sysUserService.updateUserDepart(username, orgCode);
		SysUser sysUser = sysUserService.getUserByName(username);
		JSONObject obj = new JSONObject();
		obj.put("userInfo", sysUser);
		result.setResult(obj);
		return result;
	}
	/**
	 * PC端注册并登陆  手机号，密码，邮箱
	 * @param req
	 * @return
	 */
	@ApiOperation(value = "PC端注册并登陆", notes = "PC端注册并登陆")
	@PostMapping("/register")
	public Result<?> register(@RequestBody RegisterVOReq req){
		Result<?> result = new Result<>();
		if (!req.getPassword().equals(req.getConfirmPassword())){
			result.setMessage("两次密码输入不一致，请重新输入！");
			result.setSuccess(false);
			return result;
		}
		SysUser sysUser = sysUserService.getUserByName(req.getUsername());
		if (null != sysUser){
			result.setMessage("用户名已存在，请使用账号，密码登陆！");
			result.setSuccess(false);
			return result;
		} else {
			sysUserService.insertUser(req);
			sysUser = sysUserService.getUserByName(req.getUsername());
			//添加日志
			baseCommonService.addLog("用户名: " + sysUser.getUsername() + ",登录成功！", CommonConstant.LOG_TYPE_1, null);
			return userInfo(sysUser, result);
//			result.setMessage("注册成功，请使用账号，密码登陆!");
//			result.setSuccess(true);
//			 result;
		}
	}

	/**
	 * 用户信息
	 *
	 * @param sysUser
	 * @param result
	 * @return
	 */
	private Result<?> userInfo(SysUser sysUser, Result<?> result) {
		String username = sysUser.getUsername();
		String syspassword = sysUser.getPassword();
		// 获取用户部门信息
		Map<String, Object> obj = new HashMap<>();

		// 生成token
		String token = JwtUtil.sign(username, syspassword);
		// 设置token缓存有效时间
		redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token);
		redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME * 24 / 1000);//pc端24小时
		obj.put("token", token);
		obj.put("userInfo", sysUser);

		/*List<SysDepart> departs = sysDepartService.queryUserDeparts(sysUser.getId());
		obj.put("departs", departs);
		if (departs == null || departs.size() == 0) {
			obj.put("multi_depart", 0);
		} else if (departs.size() == 1) {
			sysUserService.updateUserDepart(username, departs.get(0).getOrgCode());
			obj.put("multi_depart", 1);
		} else {
			//查询当前是否有登录部门
			// update-begin--Author:wangshuai Date:20200805 for：如果用戶为选择部门，数据库为存在上一次登录部门，则取一条存进去
			SysUser sysUserById = sysUserService.getById(sysUser.getId());
			if(oConvertUtils.isEmpty(sysUserById.getOrgCode())){
				sysUserService.updateUserDepart(username, departs.get(0).getOrgCode());
			}
			// update-end--Author:wangshuai Date:20200805 for：如果用戶为选择部门，数据库为存在上一次登录部门，则取一条存进去
			obj.put("multi_depart", 2);
		}
		obj.put("sysAllDictItems", sysDictService.queryAllDictItems());*/
		List<String> list = sysRoleService.getUserRoleType(sysUser.getId());
		obj.put("roleType", list);
		//判断当前用户是否入围top200
		obj.put("izTop200", sportsProjectService.checkIsTop200(sysUser.getId()));
//		result.setResult(obj);
//		result.success("登录成功");
		return Result.OK("登录成功", obj);
	}


	/**
	 * 获取加密字符串
	 * @return
	 */
	@GetMapping(value = "/getEncryptedString")
	public Result<Map<String,String>> getEncryptedString(){
		Result<Map<String,String>> result = new Result<Map<String,String>>();
		Map<String,String> map = new HashMap(5);
		map.put("key", EncryptedString.key);
		map.put("iv",EncryptedString.iv);
		result.setResult(map);
		return result;
	}

	/**
	 * 切换菜单表为vue3的表
	 */
	@GetMapping(value = "/switchVue3Menu")
	public Result<String> switchVue3Menu(HttpServletResponse response) {
		Result<String> res = new Result<String>();
		sysPermissionService.switchVue3Menu();
		return res;
	}

	/**
	 * 图形验证码
	 * @param sysLoginModel
	 * @return
	 */
	@ApiOperation("校验图形验证码")
	@RequestMapping(value = "/checkCaptcha", method = RequestMethod.POST)
	public Result<?> checkCaptcha(@RequestBody SysLoginModel sysLoginModel, HttpServletRequest request){
		String captcha = sysLoginModel.getCaptcha();
		String checkKey = sysLoginModel.getCheckKey();
		if(captcha==null){
			return Result.error("验证码无效");
		}
		String lowerCaseCaptcha = captcha.toLowerCase();
		log.info("lowerCaseCaptcha = " + lowerCaseCaptcha);
		log.info("checkKey = " + checkKey);
		String realKey = Md5Util.md5Encode(lowerCaseCaptcha + checkKey +  jeecgBaseConfig.getSignatureSecret(), "utf-8");
		Object checkCode = redisUtil.get(realKey);
		if(checkCode==null || !checkCode.equals(lowerCaseCaptcha)) {
			return Result.error("验证码错误");
		}
		String clientId = IpUtils.getIpAddr(request);
		//清空短信记录数量
		DySmsLimit.clearSendSmsCount(clientId);
		redisUtil.removeAll(realKey);
		return Result.ok();
	}
	/**
	 * 登录二维码
	 */
	//@ApiOperation(value = "登录二维码", notes = "登录二维码")
	@GetMapping("/getLoginQrcode")
	public Result<?>  getLoginQrcode() {
		Map<String, String> map = new HashMap<>();
		String code = IdWorker.getIdStr();
		map.put("code", code);
		String qrcodeId = "https://www.huolixiaoyuan.com/phoneLogin.html?code=" + code;
		//存放二维码唯一标识60秒有效
		redisUtil.set(CommonConstant.LOGIN_QRCODE + code, code, 300);
		try {
			// 创建二维码数据矩阵
			Map<EncodeHintType, Object> hints = new HashMap<>();
			hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); // 设置字符编码为UTF-8
			hints.put(EncodeHintType.MARGIN, 1); // 设置白边宽度为1个像素
			BitMatrix bitMatrix = new MultiFormatWriter().encode(qrcodeId, BarcodeFormat.QR_CODE, 300, 300, hints);

			// 将BitMatrix转换为BufferedImage，然后转换为Base64编码的字符串
			try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
				MatrixToImageWriter.writeToStream(bitMatrix, "PNG", baos); // 输出为PNG格式
				byte[] imageBytes = baos.toByteArray();
				String imageStr = Base64.getEncoder().encodeToString(imageBytes); // 转换为Base64编码的字符串
				map.put("imageStr", imageStr);
				return Result.OK(map);
			}
		}catch (Exception e) {
			e.printStackTrace();
			return Result.error(e.getMessage());
		}
	}

	/**
	 * 扫码二维码
	 */
	//@ApiOperation(value = "扫码登录二维码", notes = "扫码登录二维码")
	@PostMapping("/scanLoginQrcode")
	public Result<?> scanLoginQrcode(@RequestParam String qrcodeId, @RequestParam String token) {
		Object check = redisUtil.get(CommonConstant.LOGIN_QRCODE + qrcodeId);
		if (oConvertUtils.isNotEmpty(check)) {
			//存放token给前台读取
			redisUtil.set(CommonConstant.LOGIN_QRCODE_TOKEN+qrcodeId, token, 60);
		} else {
			return Result.error("二维码已过期,请刷新后重试");
		}
		return Result.OK("扫码成功");
	}


	/**
	 * 获取用户扫码后保存的token
	 */
	//@ApiOperation(value = "获取用户扫码后保存的token", notes = "获取用户扫码后保存的token")
	@GetMapping("/getQrcodeToken")
	public Result getQrcodeToken(@RequestParam String qrcodeId) {
		Object token = redisUtil.get(CommonConstant.LOGIN_QRCODE_TOKEN + qrcodeId);
		Map result = new HashMap(5);
		Object qrcodeIdExpire = redisUtil.get(CommonConstant.LOGIN_QRCODE + qrcodeId);
		if (oConvertUtils.isEmpty(qrcodeIdExpire)) {
			//二维码过期通知前台刷新
			result.put("token", "-2");
			return Result.OK(result);
		}
		if (oConvertUtils.isNotEmpty(token)) {
			result.put("success", true);
			result.put("token", token);
		} else {
			result.put("token", "-1");
		}
		return Result.OK(result);
	}

	@ApiOperation("获取授权Token用于H5本机号码校验的鉴权")
	@PostMapping("/getAliyunAuthToken")
	public Result<?> getAliyunAuthToken(@RequestBody GetAuthTokenRequest getAuthTokenRequest){
		try {
			Map<String, String> map = new HashMap<>();
			String accessToken = (String) redisUtil.get(CommonConstant.ACCESS_TOKEN);
			String jwtToken = (String) redisUtil.get(CommonConstant.JWT_TOKEN);
			if (!"".equals(accessToken) && null != accessToken){
				map.put("accessToken", accessToken);
				if (!"".equals(jwtToken) && null != jwtToken){
					map.put("jwtToken", jwtToken);
				}
			} else {
				GetAuthTokenResponse response =  AliYunUtil.getAuthToken(getAuthTokenRequest);
				String result = new Gson().toJson(response);
				JSONObject jsonObject = JSONObject.parseObject(result);
				if(jsonObject.getIntValue("statusCode") == 200){
					String body = jsonObject.getString("body");
					JSONObject bodyObj = JSONObject.parseObject(body);
					String tokenInfo = bodyObj.get("tokenInfo").toString();
					JSONObject obj = JSONObject.parseObject(tokenInfo);
					accessToken = obj.get("accessToken").toString();
					jwtToken = obj.get("jwtToken").toString();
					//业务鉴权 Token AccessToken 有效期是 10 分钟，有效期内可以重复使用。
					redisUtil.set(CommonConstant.ACCESS_TOKEN , accessToken, 600);
					//API 鉴权 Token JwtToken 有效期是 1 小时，有效期内可以重复使用。
					// 由于此接口是两者同时获取，故有效期都设置为10分钟可重复使用
					redisUtil.set(CommonConstant.JWT_TOKEN, jwtToken, 600);
					map.put("accessToken", accessToken);
					map.put("jwtToken", jwtToken);
				}
			}
			return Result.OK("success", map);
		} catch (Exception e) {
			e.printStackTrace();
			return Result.error("fail", e.getMessage());
		}
	}


	@ApiOperation("本机号码验证（H5专用),认证完成后返回认证结果")
	@PostMapping("/getVerifyPhoneWithToken")
	public Result<?> getVerifyPhoneWithToken(@RequestBody VerifyPhoneWithTokenVO verifyPhoneWithTokenVO){
		try {
			String verifyResult = "UNKNOWN";
			VerifyPhoneWithTokenRequest verifyPhoneWithTokenRequest = verifyPhoneWithTokenVO.getVerifyPhoneWithTokenRequest();
			VerifyPhoneWithTokenResponse response = AliYunUtil.verifyPhoneWithToken(verifyPhoneWithTokenRequest);
			String result = new Gson().toJson(response);
			JSONObject jsonObject = JSONObject.parseObject(result);
			if(jsonObject.getString("code").equals("OK")){
				String gateVerify = jsonObject.getString("gateVerify");
				JSONObject obj = JSONObject.parseObject(gateVerify);
				verifyResult = obj.get("verifyResult").toString();
				//成功的话，直接将用户写入数据库中
				String mobile = verifyPhoneWithTokenRequest.getPhoneNumber();
				login(mobile);
				Map<String, Object> map = new HashMap<>();
				map.put("mobile", mobile);
				map.put("status", 1);
				String redisKey = CommonConstant.H5_VERIFY_STATUS + verifyPhoneWithTokenVO.getCode();
				log.info("redis key === {}", redisKey);
				redisUtil.set(redisKey, map, 300);
			}
			return Result.OK("success", verifyResult);
		} catch (Exception e) {
			e.printStackTrace();
			return Result.error("fail", e.getMessage());
        }
    }

	public void login(String phone){
		//校验用户有效性
		SysUser sysUser = sysUserService.getUserByPhone(phone);
		if (sysUser == null) {
			sysUser = sysUserService.getUserByName(phone);
			if (sysUser == null) {
				//不存在则创建新用户
				sysUserService.addUser(phone);
			}
		}
	}

	/**
	 * status = 0，说明未通过；status =1 为通过；status =2 说明code失效
	 * @param code
	 * @return
	 */
	@ApiOperation("轮询判断H5登录状态")
	@GetMapping("/checkLogin")
	public Result<?> checkLogin(@RequestParam String code){
		Map<String, Object> map = new HashMap<>();
		String checkCode = (String) redisUtil.get(CommonConstant.LOGIN_QRCODE + code);
		//判断code是否存在，不存在说明code失效
		if (checkCode == null) {
			map.put("status", 2);
			return Result.OK("code失效",map);
		} else {
			String redisKey = CommonConstant.H5_VERIFY_STATUS + code;
			Map<String, Object> redisMap = (Map<String, Object>)redisUtil.get(redisKey);
			if (redisMap != null) {
				String phone = redisMap.get("mobile").toString();
				SysUser sysUser = sysUserService.getUserByPhone(phone);
				if (sysUser == null){
					sysUser = sysUserService.getUserByName(phone);
				}
				String username = sysUser.getUsername();
				String syspassword = sysUser.getPassword();
				// 生成token
				String token = JwtUtil.sign(username, syspassword);
				// 设置token缓存有效时间
				redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token, JwtUtil.EXPIRE_TIME * 1 / 1000);//pc端24小时
				map.put("token", token);
				map.put("userInfo", sysUser);
				map.put("sysAllDictItems", sysDictService.queryAllDictItems());
				List<String> list = sysRoleService.getUserRoleType(sysUser.getId());
				map.put("roleType", list);
				map.put("status", 1);
				redisUtil.del(redisKey);
				return Result.OK("success", map);
			}
		}
		map.put("status", 0);
		return Result.OK("pending",map);
	}

}


